﻿namespace HCore
{
    public struct Vector3
    {
        public Vector3(Fix x, Fix y, Fix z)
        {
            this.x = x;
            this.y = y;
            this.z = z;
        }

        public Fix x;

        public Fix y;

        public Fix z;

        private static readonly Vector3 zeroVector = new Vector3(0, 0, 0);
        private static readonly Vector3 oneVector = new Vector3(1, 1, 1);
        private static readonly Vector3 upVector = new Vector3(0, 1, 0);
        private static readonly Vector3 downVector = new Vector3(0, -1, 0);
        private static readonly Vector3 leftVector = new Vector3(-1, 0, 0);
        private static readonly Vector3 rightVector = new Vector3(0, 0, 1);
        private static readonly Vector3 forwardVector = new Vector3(0, 0, 1);
        private static readonly Vector3 backVector = new Vector3(0, 0, -1);

        public Vector3(Fix x, Fix y)
        {
            this.x = x;
            this.y = y;
            this.z = Fix.Zero;
        }

        public override bool Equals(object other) => other is Vector3 other1 && this.Equals(other1);

        public bool Equals(Vector3 other) => this.x == other.x && this.y == other.y && this.z == other.z;

        public override int GetHashCode() =>
            this.x.GetHashCode() ^ this.y.GetHashCode() << 2 ^ this.z.GetHashCode() >> 2;

        public static Vector3 Normalize(Vector3 value)
        {
            Fix num = Vector3.Magnitude(value);
            return num > Fix.Zero ? value / num : Vector3.zero;
        }

        public void Normalize()
        {
            Fix num = Vector3.Magnitude(this);
            if (num.Raw > 0)
                this = this / num;
            else
                this = Vector3.zero;
        }

        public Vector3 normalized => Vector3.Normalize(this);

        public static Fix Dot(Vector3 lhs, Vector3 rhs) => lhs.x * rhs.x + lhs.y * rhs.y + lhs.z * rhs.z;

        public static Vector3 Cross(Vector3 lhs, Vector3 rhs) => new Vector3(lhs.y * rhs.z - lhs.z * rhs.y,
            lhs.z * rhs.x - lhs.x * rhs.z, lhs.x * rhs.y - lhs.y * rhs.x);

        public static Fix Distance(Vector3 a, Vector3 b)
        {
            Fix num1 = a.x - b.x;
            Fix num2 = a.y - b.y;
            Fix num3 = a.z - b.z;
            return FixMath.Sqrt(num1 * num1 + num2 * num2 + num3 * num3);
        }

        public static Fix Magnitude(Vector3 vector) =>
            FixMath.Sqrt(vector.x * vector.x + vector.y * vector.y + vector.z * vector.z);

        public Fix magnitude => FixMath.Sqrt(this.x * this.x + this.y * this.y + this.z * this.z);

        public static Fix SqrMagnitude(Vector3 vector) =>
            vector.x * vector.x + vector.y * vector.y + vector.z * vector.z;

        public Fix sqrMagnitude => this.x * this.x + this.y * this.y + this.z * this.z;

        public static Vector3 zero => Vector3.zeroVector;
        public static Vector3 one => Vector3.oneVector;
        public static Vector3 forward => Vector3.forwardVector;
        public static Vector3 back => Vector3.backVector;
        public static Vector3 up => Vector3.upVector;
        public static Vector3 down => Vector3.downVector;
        public static Vector3 left => Vector3.leftVector;
        public static Vector3 right => Vector3.rightVector;

        public static Vector3 operator +(Vector3 a, Vector3 b) => new Vector3(a.x + b.x, a.y + b.y, a.z + b.z);

        public static Vector3 operator -(Vector3 a, Vector3 b) => new Vector3(a.x - b.x, a.y - b.y, a.z - b.z);

        public static Vector3 operator -(Vector3 a) => new Vector3(-a.x, -a.y, -a.z);

        public static Vector3 operator *(Vector3 a, Fix d) => new Vector3(a.x * d, a.y * d, a.z * d);

        public static Vector3 operator *(Fix d, Vector3 a) => new Vector3(a.x * d, a.y * d, a.z * d);

        public static Vector3 operator /(Vector3 a, Fix d) => new Vector3(a.x / d, a.y / d, a.z / d);

        public static bool operator ==(Vector3 lhs, Vector3 rhs)
        {
            Fix num1 = lhs.x - rhs.x;
            Fix num2 = lhs.y - rhs.y;
            Fix num3 = lhs.z - rhs.z;
            return num1 * num1 + num2 * num2 + num3 * num3 == Fix.Zero;
        }

        public static bool operator !=(Vector3 lhs, Vector3 rhs) => !(lhs == rhs);

        public override string ToString() => string.Format("({0:F1}, {1:F1}, {2:F1})", (object) this.x, (object) this.y,
            (object) this.z);

        public void Truncate(Fix max)
        {
            if (magnitude > max)
            {
                this = normalized * max;
            }
        }
    }
}